home *** CD-ROM | disk | FTP | other *** search
/ Aminet 34 / Aminet 34 (2000)(Schatztruhe)[!][Dec 1999].iso / Aminet / util / gnu / unixcmds.lha / unixcmds / src / fgrep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-06  |  11.3 KB  |  392 lines

  1. /* fgrep - fast grep                    Author: Bert Gijsbers */
  2. /* JF Fabre added Amiga Wildcard support */
  3.  
  4. /* Copyright (c) 1991 by Bert Gijsbers.  All rights reserved.
  5.  * Permission to use and redistribute this software is hereby granted provided
  6.  * that this copyright notice remains intact and that any modifications are
  7.  * clearly marked as such.
  8.  *
  9.  * syntax:
  10.  *      fgrep -chlnsv <[-e string] ... [-f file] ... | string> [file] ...
  11.  * options:
  12.  *      -c : print the number of matching lines
  13.  *      -h : don't print file name headers if more than one file
  14.  *      -l : print only the file names of the files containing a match
  15.  *      -n : print line numbers
  16.  *      -s : don't print, return status only
  17.  *      -v : reverse, lines not containing one of the strings match
  18.  *      -e string : search for this string
  19.  *      -f file : file contains strings to search for
  20.  * notes:
  21.  *      Options are processed by getopt(3).
  22.  *      Multiple strings per command line are supported, eg.
  23.  *              fgrep -e str1 -e str2 *.c
  24.  *      Instead of a filename - is allowed, meaning standard input.
  25.  */
  26.  
  27. /* #include <ansi.h> */
  28. #include <sys/types.h>
  29. #include <unistd.h>
  30. #include <fcntl.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <stdio.h>
  34. #include "getopt.h"
  35. #include "amigawildcard.h"
  36.  
  37. #define MAX_STR_LEN      256    /* maximum length of strings to search for */
  38. #define BYTE            0xFF    /* convert from char to int */
  39. #define READ_SIZE       4096    /* read() request size */
  40. #define BUF_SIZE (2*READ_SIZE)  /* size of buffer */
  41.  
  42. typedef struct test_str {
  43.   struct test_str *next;        /* linked list */
  44.   char *str;                    /* string to be found */
  45.   char *str_end;                /* points to last character */
  46.   int len;                      /* string length */
  47.   char *bufp;                   /* pointer into input buffer */
  48.   unsigned char table[256];     /* table for Boyer-Moore algorithm */
  49. } test_str;
  50.  
  51. test_str *strings;
  52. char *prog_name;
  53. int cflag, hflag, lflag, nflag, sflag, vflag;
  54. unsigned line_num;              /* line number in current file */
  55.  
  56. int fd_in, eof_seen;            /* file descriptor for input and eof status */
  57. char input_buffer[BUF_SIZE + 2];/* buffer + sentinel margin */
  58. #define buffer  (&input_buffer[2])
  59.  
  60. /* Pointers into the input buffer */
  61. char *input;                    /* points to current input char */
  62. char *max_input;                /* points to first invalid char */
  63. char *buf_end;                  /* points to first char not read */
  64.  
  65. /* Error messages */
  66. char no_mem[] = "not enough memory";
  67. char no_arg[] = "argument missing";
  68.  
  69. extern char *optarg;
  70. extern int optind;
  71.  
  72. int main  (int argc, char **argv);
  73. char *search_str  (test_str * ts);
  74. int fill_buffer  (void);
  75. void failure  (char *mesg);
  76. void file_open  (void);
  77. void usage  (void);
  78. char *get_line  (void);
  79. void string_file  (void);
  80. void add_string  (char *str);
  81. int getopt  (int argc, char **argv, char *optstring);
  82.  
  83. int main(argc, argv)
  84. /* [<][>][^][v][top][bottom][index][help] */
  85. int argc;
  86. char **argv;
  87. {
  88.   char *line;
  89.   int c;
  90.   unsigned count;               /* number of matching lines in current file */
  91.   unsigned found_one = 0;       /* was there any match in any file at all ? */
  92.   int argend,argstart,i;
  93.   t_strlist strargs;
  94.  
  95. #ifdef noperprintf
  96.   noperprintf(stdout);
  97. #else
  98.   static char outbuf[BUFSIZ];
  99.  
  100.   setvbuf(stdout, outbuf, _IOFBF, sizeof outbuf);
  101. #endif
  102.  
  103.   prog_name = argv[0];
  104.   if (argc == 1) usage();
  105.   while ((c = getopt(argc, argv, "ce:f:hlnsv")) != EOF) {
  106.         switch (c) {
  107.             case 'c':   cflag++;        break;
  108.             case 'e':   add_string(optarg);     break;
  109.             case 'f':   string_file();  break;
  110.             case 'h':   hflag++;        break;
  111.             case 'l':   lflag++;        break;
  112.             case 'n':   nflag++;        break;
  113.             case 's':   sflag++;        break;
  114.             case 'v':   vflag++;        break;
  115.             default:    usage();        break;
  116.         }
  117.   }
  118.  
  119.   /* If no -e or -f option is used take a string from the command line. */
  120.   if (strings == (test_str *) NULL) {
  121.         if (optind == argc) failure(no_arg);
  122.         add_string(argv[optind++]);
  123.   }
  124.  
  125.   /* scan for pattern */
  126.  
  127.   init_list(&strargs);
  128.  
  129.   for (i=optind;i<argc;i++)
  130.   {
  131.     scan_pattern(argv[i],&strargs);
  132.   }
  133.  
  134.   argend=strargs.len;
  135.   argstart=0;
  136.  
  137.   if (strargs.len < 2)
  138.         hflag++;                /* don't print filenames if less than two
  139.                          * files */
  140.  
  141.   /* Handle every matching line according to the flags. */
  142.   do {
  143.         optarg = pop_elt(argstart,&strargs);
  144.  
  145.         if (optarg==NULL)
  146.         {
  147.           perror("optarg=NULL");exit(1);
  148.         }
  149.  
  150.         file_open();
  151.         count = 0;
  152.         while ((line = get_line()) != (char *) NULL) {
  153.                 count++;
  154.                 if (sflag) return 0;
  155.                 if (lflag) {
  156.                         printf("%s\n", optarg);
  157.                         fflush(stdout);
  158.                         break;
  159.                 }
  160.                 if (cflag) continue;
  161.                 if (hflag == 0) printf("%s:", optarg);
  162.                 if (nflag) printf("%u:", line_num);
  163.                 do {
  164.                         putchar(*line);
  165.                 } while (++line < input);
  166.                 fflush(stdout);
  167.         }
  168.         found_one |= count;
  169.         if (cflag) {
  170.                 if (hflag == 0) printf("%s: ", optarg);
  171.                 printf("%u\n", count);
  172.                 fflush(stdout);
  173.         }
  174.         close(fd_in);
  175.   } while (++argstart < argend);
  176.  
  177.   clear_list(&strargs);
  178.  
  179.   /* Exit nonzero if no match is found. */
  180.   return found_one ? 0 : 1;
  181. }
  182.  
  183. void usage()
  184. /* [<][>][^][v][top][bottom][index][help] */
  185. {
  186.   fprintf(stderr,
  187.         "Usage: %s -chlnsv <[-e string] ... [-f file] ... | string> [file] ...\n",
  188.         prog_name);
  189.   exit(2);
  190. }
  191.  
  192. void failure(mesg)
  193. /* [<][>][^][v][top][bottom][index][help] */
  194. char *mesg;
  195. {
  196.   fprintf(stderr, "%s: %s\n", prog_name, mesg);
  197.   exit(1);
  198. }
  199.  
  200. /* Add a string to search for to the global linked list `strings'. */
  201. void add_string(str)
  202. /* [<][>][^][v][top][bottom][index][help] */
  203. char *str;
  204. {
  205.   test_str *ts;
  206.   int len;
  207.  
  208.   if (str == (char *) NULL || (len = strlen(str)) == 0) return;
  209.   if (len > MAX_STR_LEN) failure("string too long");
  210.   if ((ts = (test_str *) malloc(sizeof(*ts))) == (test_str *) NULL)
  211.         failure(no_mem);
  212.  
  213.   /* Initialize Boyer-Moore table. */
  214.   memset(ts->table, len, sizeof(ts->table));
  215.   ts->len = len;
  216.   ts->str = str;
  217.   ts->str_end = str + len - 1;
  218.   for (; --len >= 0; str++) ts->table[*str & BYTE] = len;
  219.  
  220.   /* Put it on the list */
  221.   ts->next = strings;
  222.   strings = ts;
  223. }
  224.  
  225. /* Open a file for reading.  Initialize input buffer pointers. */
  226. void file_open()
  227. /* [<][>][^][v][top][bottom][index][help] */
  228. {
  229.   /* Use stdin if no file arguments are given on the command line. */
  230.   if (optarg == (char *) NULL || strcmp(optarg, "-") == 0) {
  231.         fd_in = 0;
  232.         optarg = "stdin";
  233.   } else if ((fd_in = open(optarg, O_RDONLY)) == -1) {
  234.         fprintf(stderr, "%s: can't open %s\n", prog_name, optarg);
  235.         exit(1);
  236.   }
  237.   input = max_input = buf_end = buffer;
  238.   buffer[-1] = '\n';            /* sentinel */
  239.   eof_seen = 0;
  240.   line_num = 0;
  241. }
  242.  
  243. /* Move any leftover characters to the head of the buffer.
  244.  * Read characters into the rest of the buffer.
  245.  * Round off the available input to whole lines.
  246.  * Return the number of valid input characters.
  247.  */
  248. int fill_buffer()
  249. /* [<][>][^][v][top][bottom][index][help] */
  250. {
  251.   char *bufp;
  252.   int size;
  253.  
  254.   if (eof_seen) return 0;
  255.  
  256.   size = buf_end - max_input;
  257.   memmove(buffer, max_input, size);
  258.   bufp = &buffer[size];
  259.  
  260.   do {
  261.         if ((size = read(fd_in, bufp, READ_SIZE)) <= 0) {
  262.                 if (size != 0) failure("read error");
  263.                 eof_seen++;
  264.                 if (bufp == buffer)     /* no input left */
  265.                         return 0;
  266.                 /* Make sure the last char of a file is '\n'. */
  267.                 *bufp++ = '\n';
  268.                 break;
  269.         }
  270.         bufp += size;
  271.   } while (bufp - buffer < READ_SIZE && bufp[-1] != '\n');
  272.  
  273.   buf_end = bufp;
  274.   while (*--bufp != '\n');
  275.   if (++bufp == buffer) {
  276.         /* Line too long. */
  277.         *buf_end++ = '\n';
  278.         bufp = buf_end;
  279.   }
  280.   max_input = bufp;
  281.   input = buffer;
  282.  
  283.   return max_input - buffer;
  284. }
  285.  
  286. /* Read strings from a file.  Give duplicates to add_string(). */
  287. void string_file()
  288. /* [<][>][^][v][top][bottom][index][help] */
  289. {
  290.   char *str, *p;
  291.  
  292.   file_open();
  293.   while (input < max_input || fill_buffer() > 0) {
  294.         p = (char *) memchr(input, '\n', BUF_SIZE);
  295.         *p++ = '\0';
  296.         if ((str = (char *) malloc(p - input)) == (char *) NULL)
  297.                 failure(no_mem);
  298.         memcpy(str, input, p - input);
  299.         add_string(str);
  300.         input = p;
  301.   }
  302.   close(fd_in);
  303. }
  304.  
  305. /* Scan the rest of the available input for a string using Boyer-Moore.
  306.  * Return a pointer to the match or a pointer beyond end of input if no match.
  307.  * Record how far the input is scanned.
  308.  */
  309. char *search_str(ts)
  310. /* [<][>][^][v][top][bottom][index][help] */
  311. test_str *ts;
  312. {
  313.   char *bufp, *prevbufp, *s;
  314.  
  315.   bufp = input + ts->len - 1;
  316.   while (bufp < max_input) {
  317.         prevbufp = bufp;
  318.         bufp += ts->table[*bufp & BYTE];
  319.         if (bufp > prevbufp) continue;
  320.         s = ts->str_end;
  321.         do {
  322.                 if (s == ts->str) {     /* match found */
  323.                         ts->bufp = bufp;
  324.                         return bufp;
  325.                 }
  326.         } while (*--bufp == *--s);
  327.         bufp = prevbufp + 1;
  328.   }
  329.   ts->bufp = bufp;
  330.  
  331.   return bufp;
  332. }
  333.  
  334. /* Return the next line in which one of the strings occurs.
  335.  * Or, if the -v option is used, the next line without a match.
  336.  * Or NULL on EOF.
  337.  */
  338. char *get_line()
  339. /* [<][>][^][v][top][bottom][index][help] */
  340. {
  341.   test_str *ts;
  342.   char *match, *line;
  343.  
  344.   /* Loop until a line is found. */
  345.   while (1) {
  346.         if (input >= max_input && fill_buffer() == 0) { /* EOF */
  347.                 line = (char *) NULL;
  348.                 break;
  349.         }
  350.  
  351.         /* If match is still equal to max_input after the next loop
  352.          * then no match is found. */
  353.         match = max_input;
  354.         ts = strings;
  355.         do {
  356.                 if (input == buffer) {
  357.                         if (search_str(ts) < match) match = ts->bufp;
  358.                 } else if (ts->bufp < match) {
  359.                         if (ts->bufp >= input || search_str(ts) < match)
  360.                                 match = ts->bufp;
  361.                 }
  362.         } while ((ts = ts->next) != (test_str *) NULL);
  363.  
  364.         /* Determine if and in what line a match is found. Only do
  365.          * line number counting if it is necessary or very easy. */
  366.         if (vflag) {
  367.                 line_num++;
  368.                 line = input;
  369.                 input = 1 + (char *) memchr(line, '\n', BUF_SIZE);
  370.                 if (input <= match) break;      /* no match in current line */
  371.         } else if (nflag) {
  372.                 do {
  373.                         line_num++;
  374.                         line = input;
  375.                         input = 1 + (char *) memchr(line, '\n', BUF_SIZE);
  376.                 } while (input < match ||
  377.                          (input == match && match < max_input));
  378.                 if (match < max_input) break;   /* match found */
  379.         } else if (match < max_input) {
  380.                 /* Match found. */
  381.                 for (line = match; *--line != '\n';);
  382.                 line++;
  383.                 input = 1 + (char *) memchr(match, '\n', BUF_SIZE);
  384.                 break;
  385.         } else
  386.                 input = max_input;
  387.   }
  388.  
  389.   return line;
  390. }
  391. /* [<][>][^][v][top][bottom][index][help] */
  392.